/*
 * Copyright 2011-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0.
 * See `LICENSE` in the project root for license information.
 */

package me.ijleex.platform.framework.json.util;

import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonInclude;

import tools.jackson.core.JacksonException;
import tools.jackson.core.json.JsonFactory;
import tools.jackson.core.json.JsonReadFeature;
import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.JavaType;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.SerializationFeature;
import tools.jackson.databind.ext.javatime.deser.LocalDateDeserializer;
import tools.jackson.databind.ext.javatime.deser.LocalDateTimeDeserializer;
import tools.jackson.databind.ext.javatime.deser.LocalTimeDeserializer;
import tools.jackson.databind.ext.javatime.ser.LocalDateSerializer;
import tools.jackson.databind.ext.javatime.ser.LocalDateTimeSerializer;
import tools.jackson.databind.ext.javatime.ser.LocalTimeSerializer;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.module.SimpleModule;
import tools.jackson.databind.ser.std.ToStringSerializer;
import tools.jackson.databind.type.CollectionType;
import tools.jackson.databind.type.MapType;
import tools.jackson.databind.type.TypeFactory;

/**
 * Jackson 工具类.
 *
 * @author liym
 * @since 2019-01-18 15:11
 */
public final class JacksonUtils {

    private static final Logger logger = LoggerFactory.getLogger(JacksonUtils.class);

    private JacksonUtils() {
        throw new IllegalAccessError("Utility class");
    }

    /**
     * 将对象转为 JSON 字符串
     *
     * @param obj 对象
     * @return JSON
     */
    public static String objToJson(Object obj) {
        try {
            return Lazy.OBJECT_MAPPER.writeValueAsString(obj);
        } catch (JacksonException e) {
            logger.error("objToJson error", e);
            throw new IllegalArgumentException("objToJson error", e);
        }
    }

    /**
     * 将 JSON 字符串转对象
     *
     * @param jsonStr JSON
     * @param valueType 对象类型
     * @param <T> 对象类型
     * @return 对象
     */
    public static <T> T jsonToObj(String jsonStr, Class<T> valueType) {
        try {
            return Lazy.OBJECT_MAPPER.readValue(jsonStr, valueType);
        } catch (JacksonException e) {
            logger.error("jsonToObj error", e);
            throw new IllegalArgumentException("jsonToObj error", e);
        }
    }

    /**
     * 将 JSON 字符串转对象
     *
     * @param jsonStr JSON
     * @param valueType 对象类型
     * @param <T> 对象类型
     * @return 对象
     */
    private static <T> T jsonToObj(String jsonStr, JavaType valueType) {
        try {
            return Lazy.OBJECT_MAPPER.readValue(jsonStr, valueType);
        } catch (JacksonException e) {
            logger.error("jsonToObj error", e);
            throw new IllegalArgumentException("jsonToObj error", e);
        }
    }

    /**
     * 将 JSON 字符串转 Map
     *
     * @param jsonStr JSON
     * @return {@code Map<String, Object>}
     * @see #jsonToMap(String, Class)
     */
    public static Map<String, Object> jsonToMap(String jsonStr) {
        return jsonToMap(jsonStr, Object.class);
    }

    /**
     * 将 JSON 字符串转 Map
     *
     * @param jsonStr JSON
     * @param valueType 值类型
     * @param <T> 值类型
     * @return {@code Map<String, T>}
     * @see #jsonToMap(String, Class, Class)
     */
    public static <T> Map<String, T> jsonToMap(String jsonStr, Class<T> valueType) {
        return jsonToMap(jsonStr, String.class, valueType);
    }

    /**
     * 将 JSON 字符串转 Map
     *
     * @param jsonStr JSON
     * @param keyType 键类型
     * @param valueType 值类型
     * @param <K> 键类型
     * @param <V> 值类型
     * @return {@code Map<K, V>}
     * @see TypeFactory#constructMapType(Class, Class, Class)
     * @since 2021-06-08 13:03
     */
    public static <K, V> Map<K, V> jsonToMap(String jsonStr, Class<K> keyType, Class<V> valueType) {
        try {
            MapType mapType = getTypeFactory().constructMapType(Map.class, keyType, valueType);
            return jsonToObj(jsonStr, mapType);
        } catch (Exception e) {
            logger.error("jsonToMap error", e);
            return Collections.emptyMap();
        }
    }

    /**
     * 将 JSON 字符串转 List
     *
     * @param jsonArrayStr JSON 数组
     * @param valueType 值类型
     * @param <T> 值类型
     * @return {@code List<T>}
     * @see TypeFactory#constructCollectionType(Class, Class)
     */
    public static <T> List<T> jsonToList(String jsonArrayStr, Class<T> valueType) {
        try {
            CollectionType listType = getTypeFactory().constructCollectionType(List.class, valueType);
            return jsonToObj(jsonArrayStr, listType);
        } catch (Exception e) {
            logger.error("jsonToList error", e);
            return Collections.emptyList();
        }
    }

    /**
     * 转换Map
     *
     * @param map 待转换Map
     * @param valueType 目标对象类型
     * @param <T> 目标对象类型
     * @return 目标对象
     */
    public static <T> T mapToObj(Map<?, ?> map, Class<T> valueType) {
        return convertObj(map, valueType);
    }

    /**
     * 转换List
     *
     * @param list 待转换List
     * @param valueType 目标List值类型
     * @param <T> 目标List值类型
     * @return 目标List
     * @since 2023-04-16 23:08
     */
    public static <T> List<T> convertList(List<?> list, Class<T> valueType) {
        if (list == null || list.isEmpty()) {
            return Collections.emptyList();
        }
        List<T> retList = new ArrayList<>(list.size());
        for (Object obj : list) {
            T retVal = convertObj(obj, valueType);
            retList.add(retVal);
        }
        return retList;
    }

    /**
     * 转换对象类型
     *
     * @param obj 待转换对象
     * @param toValueType 目标对象类型
     * @param <T> 目标对象类型
     * @return 目标对象
     * @since 2023-04-16 23:05
     */
    public static <T> T convertObj(Object obj, Class<T> toValueType) {
        return Lazy.OBJECT_MAPPER.convertValue(obj, toValueType);
    }

    /**
     * 获取类型工厂
     *
     * @return {@link TypeFactory}
     * @since 2019-01-09 11:16
     */
    private static TypeFactory getTypeFactory() {
        return Lazy.OBJECT_MAPPER.getTypeFactory();
    }

    private static class Lazy {

        private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
        private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
        private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");

        /**
         * Jackson JSON 处理
         */
        private static final ObjectMapper OBJECT_MAPPER = createObjectMapper();

        private Lazy() {
            throw new IllegalAccessError("No instance");
        }

        /**
         * @see org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration.JacksonJsonMapperBuilderCustomizerConfiguration.StandardJsonMapperBuilderCustomizer#customize
         */
        private static ObjectMapper createObjectMapper() {
            JsonFactory factory = JsonFactory.builder()
                    // 允许出现单引号
                    .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES)
                    // 允许出现特殊字符和转义符
                    .enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS)
                    // 自动处理反斜线
                    .enable(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)
                    // 构建工厂
                    .build();

            SimpleModule module = new SimpleModule();
            module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER));
            module.addSerializer(LocalDate.class, new LocalDateSerializer(DATE_FORMATTER));
            module.addSerializer(LocalTime.class, new LocalTimeSerializer(TIME_FORMATTER));
            module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER));
            module.addDeserializer(LocalDate.class, new LocalDateDeserializer(DATE_FORMATTER));
            module.addDeserializer(LocalTime.class, new LocalTimeDeserializer(TIME_FORMATTER));

            // 将Long类型序列化为String类型，避免超出（-(2^53-1) ～ (2^53-1)）范围的数值序列化时精度丢失
            module.addSerializer(Long.class, ToStringSerializer.instance);
            module.addSerializer(Long.TYPE, ToStringSerializer.instance);
            module.addSerializer(BigInteger.class, ToStringSerializer.instance);

            return JsonMapper.builder(factory)
                    // 设置日期格式
                    .defaultDateFormat(new SimpleDateFormat(DATE_TIME_PATTERN))
                    // 设置为中国上海时区
                    .defaultTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
                    // 区域设置
                    .defaultLocale(Locale.CHINA)
                    // 空值不序列化
                    .changeDefaultPropertyInclusion(handler -> handler.withValueInclusion(JsonInclude.Include.NON_NULL))
                    // 空对象不要抛异常
                    .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
                    // 反序列化时，属性不存在的兼容处理
                    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                    // 添加DateTime与数字序列化配置模块
                    .addModule(module)
                    .build();
        }

    }

}
